import {createServer, Socket} from "net";
import log from "./logger";
import {EventEmitter} from "../node-test";
import utils from './utils'
import {Controller} from "./interfaces";
import settings from "./settings";

const queryCMD = new Uint8Array([0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x3d, 0xc9])
const queryDOCMD = new Uint8Array([0x01, 0x01, 0x00, 0x10, 0x00, 0x04, 0x3c, 0x0c])
const open1CMD = new Uint8Array([0x01, 0x05, 0x00, 0x10, 0xff, 0x00, 0x8d, 0xff])
const close1CMD = new Uint8Array([0x01, 0x05, 0x00, 0x10, 0x00, 0x00, 0xcc, 0x0f])
const open2CMD = new Uint8Array([0x01, 0x05, 0x00, 0x11, 0xff, 0x00, 0xdc, 0x3f])
const close2CMD = new Uint8Array([0x01, 0x05, 0x00, 0x11, 0x00, 0x00, 0x9d, 0xcf])
const open3CMD = new Uint8Array([0x01, 0x05, 0x00, 0x12, 0xff, 0x00, 0x2c, 0x3f])
const close3CMD = new Uint8Array([0x01, 0x05, 0x00, 0x12, 0x00, 0x00, 0x6d, 0xcf])
const open4CMD = new Uint8Array([0x01, 0x05, 0x00, 0x13, 0xff, 0x00, 0x7d, 0xff])
const close4CMD = new Uint8Array([0x01, 0x05, 0x00, 0x13, 0x00, 0x00, 0x3c, 0x0f])

class ControllerImpl extends EventEmitter implements Controller {
    gClient: Socket

    public R1 = false
    public R2 = false
    R3 = false
    R4 = false
    D1 = false
    D2 = false
    D3 = false
    D4 = false
    lastTimestamp = 0
    ignore = false
    sendTimer: any

    async openGateway(inout: string) {
        log.info("白名单通过:", inout);
        // inout 标识不存在的情况？
        if (!inout) {
            await this.openRL(1);
            await utils.sleep(500);
            await this.openRL(2);
        } else {
            if (inout === 'in') {
                await this.openRL(1);
                await utils.sleep(500);
                await this.openRL(2);
            } else {
                await this.openRL(2);
                await utils.sleep(500);
                await this.openRL(1);
            }
        }
    }

    closeWaitUpTimeout(inout: 'in' | 'out') {
        if (inout === 'in') {
            this.openRL(3);
        } else {
            this.openRL(4);
        }
    }

    openUpInout(inout: 'in' | 'out') {
        if (inout === 'in') {
            this.openRL(1);
        } else {
            this.openRL(2);
        }
    }

    openDownInout(inout: 'in' | 'out') {
        if (inout === 'in') {
            this.onlyOpenRL(2);
        } else {
            this.onlyOpenRL(1);
        }
    }

    async closeDownInout(inout: 'in' | 'out') {
        await this.onlyCloseRL(inout === 'in' ? 2 : 1)
        await utils.sleep(1000);
        await this.openRL(inout === 'in' ? 4 : 3)
    }

    async send(data: any) {
        if (this.gClient) {
            this.gClient.pause()
            let res
            let count = 1
            while (count < 6) {
                count++
                this.gClient.write(data);
                await utils.sleep(500)
                res = this.gClient.read()
                if (res) {
                    break
                }
            }
            this.gClient.resume()
        }
    }

    async sendSync(data: any) {
        if (this.gClient) {
            this.gClient.pause()
            let res
            let count = 1
            while (count < 6) {
                count++
                this.gClient.write(data);
                await utils.sleep(500)
                res = this.gClient.read()
                if (res) {
                    break
                }
            }
            this.gClient.resume()
            return res
        }
    }

    async openRL(index: number) {
        log.info('openRL:' + index)
        clearInterval(this.sendTimer)
        this.sendTimer = undefined
        if (index === 1) {
            await this.send(open1CMD)
            await utils.sleep(1000)
            await this.send(close1CMD)
        } else if (index === 2) {
            await this.send(open2CMD)
            await utils.sleep(1000)
            await this.send(close2CMD)
        } else if (index === 3) {
            await this.send(open3CMD)
            await utils.sleep(1000)
            await this.send(close3CMD)
        } else if (index === 4) {
            await this.send(open4CMD)
            await utils.sleep(1000)
            await this.send(close4CMD)
        }
        this.startSendTimer()

    }

    async onlyOpenRL(index: number) {
        log.info('onlyOpenRL:' + index)
        clearInterval(this.sendTimer)
        this.sendTimer = undefined
        if (index === 1) {
            await this.send(open1CMD)
        } else if (index === 2) {
            await this.send(open2CMD)
        } else if (index === 3) {
            await this.send(open3CMD)
        } else if (index === 4) {
            await this.send(open4CMD)
        }
        this.startSendTimer()
    }

    async onlyCloseRL(index: number) {
        log.info('onlyCloseRL:' + index)
        clearInterval(this.sendTimer)
        this.sendTimer = undefined
        if (index === 1) {
            await this.send(close1CMD)
        } else if (index === 2) {
            await this.send(close2CMD)
        } else if (index === 3) {
            await this.send(close3CMD)
        } else if (index === 4) {
            await this.send(close4CMD)
        }
        this.startSendTimer()
    }

    startSendTimer() {
        if (this.sendTimer === undefined) {
            this.sendTimer = setInterval(async () => {
                const r = await this.sendSync(queryCMD);
                if (r && r[0] === 1 && r[1] === 1) {
                    this.R1 = (r[3] & 1) === 1
                    this.R2 = ((r[3] >> 1) & 1) === 1
                    this.R3 = ((r[3] >> 2) & 1) === 1
                    this.R4 = ((r[3] >> 3) & 1) === 1
                    if (settings.normalOpen) {
                        this.R1 = !this.R1
                        this.R2 = !this.R2
                    }
                    this.emit('active', [this.R1, this.R2, this.R3, this.R4])
                    this.lastTimestamp = new Date().getTime();
                }
                if (this.ignore) {
                    this.R1 = this.R2 = true;
                }
                await utils.sleep(10000)
                const d = await this.sendSync(queryDOCMD)
                if (d && d[0] === 1 && d[1] === 1) {
                    this.D1 = (d[3] & 1) === 1
                    this.D2 = ((d[3] >> 1) & 1) === 1
                    this.D3 = ((d[3] >> 2) & 1) === 1
                    this.D4 = ((d[3] >> 3) & 1) === 1
                    this.emit('activeD', [this.D1, this.D2, this.D3, this.D4])
                    log.info('activeD:', this.D1, this.D2, this.D3, this.D4)
                    this.lastTimestamp = new Date().getTime();
                }
            }, 20000);
        }
    }

    init() {
        this.ignore = settings.ignore;
        const server = createServer((client: Socket) => {
            this.gClient = client
            log.info('Client remote= %s:%s', client.remoteAddress, client.remotePort);
            // client.setTimeout(500);
            client.on('data', (buffer) => {
                // 监测主动上传
                if (buffer[0] === 0) {
                    if (buffer[3] - 16 === 0) {
                        this.R1 = buffer[4] === 255
                    }
                    if (buffer[3] - 16 === 1) {
                        this.R2 = buffer[4] === 255
                    }
                    if (buffer[3] - 16 === 2) {
                        this.R3 = buffer[4] === 255
                    }
                    if (buffer[3] - 16 === 3) {
                        this.R4 = buffer[4] === 255
                    }
                    if (settings.normalOpen) {
                        this.R1 = !this.R1
                        this.R2 = !this.R2
                    }
                    log.info("控制器主动上传", this.R1, this.R2, this.R3, this.R4)
                }
                if (this.ignore) {
                    this.R1 = this.R2 = true
                }
                this.emit('active', [this.R1, this.R2, this.R3, this.R4])
                this.lastTimestamp = new Date().getTime();
            });
            client.on('end', function () {
                log.info('Client disconnected');
                server.getConnections(function (err, count) {
                    log.info('Remaining Connection:' + count);
                });
            });
            client.on('error', function (err) {
                log.info('Socket Error:', JSON.stringify(err));
            });
            client.on('timeout', function () {
                log.info('Socket Timed Out');
            });
        });

        server.listen(5000, function () {
            log.info('Server listening: ' + JSON.stringify(server.address()));
            server.on('close', function () {
                log.info('Server Terminated');
            });
            server.on('error', function (err) {
                log.info('Server Error:', JSON.stringify(err));
            });
        });
        this.startSendTimer()
        // 30秒检测一次，60秒没数据就变遮挡
        setInterval(() => {
            const currentTimestamp = new Date().getTime()
            if (currentTimestamp - this.lastTimestamp > 60 * 1000) {
                if (this.ignore) {
                    this.R1 = this.R2 = true
                } else {
                    this.R1 = this.R2 = this.R3 = this.R4 = false
                }
                this.emit('active', [this.R1, this.R2, this.R3, this.R4])
            }
        }, 30000);
    }
}

const controller = new ControllerImpl();

export default controller;